﻿using System;
using System.Collections.Generic;

namespace DigitalQuran.Qnl
{   
    public struct PathPart
    {
        public QuranObjectType Object { get; set; }
        public int Value { get; set; }

        public override string ToString()
        {
            return Object.ToString() + " " + Value.ToString();
        }
    }

    /// <summary>
    /// Tree to Hold Parsing Information
    /// </summary>
    public sealed class ParseTree
    {
        public ParseErrors Errors { get; private set; }
        public List<ValueSpan> Values { get; private set; }
        public List<PathPart> Path { get; private set; }        

        public ParseTree()
        {
            Path = new List<PathPart>();
            Errors = new ParseErrors();
            Values = new List<ValueSpan>();
        }

        internal void Reset()
        {
            Values.Clear();            
            Path.Clear();
            qObject = Book.Instance;            
            Errors.Clear();
            allValues.Clear();
        }

        List<int> allValues = new List<int>();
        internal void AddValue(Token token1, Token token2)
        {
            QuranObjectType type = Path[Path.Count - 1].Object;
            int lLimit = AssertObjectValue(token1, type);

            ValueSpan vSpan = new ValueSpan()
            {
                LowerLimit = lastObject.GetObject(type, lLimit - 1).GlobalNumber,
            };

            if (token2 != null)
            {
                int uLimit = AssertObjectValue(token2, type);

                if (lLimit >= uLimit)
                    throw new QnlException("Upper limit must be greater then lower limit", token1.Index, token2.Index - token1.Index + token2.Length);

                vSpan.UpperLimit = lastObject.GetObject(type, uLimit - 1).GlobalNumber;
            }

            for (int i = vSpan.UpperLimit.GetValueOrDefault(vSpan.LowerLimit); i >= vSpan.LowerLimit; i--)
            {
                if (!allValues.Contains(i))
                    allValues.Add(i);
                else
                    throw new QnlException("'" + i + "' is considered revised", token1.Index, (token2 != null) ? token2.Index - token1.Index + token2.Length : token1.Length);
            }

            Values.Add(vSpan);
        }

        int AssertObjectValue(Token token, QuranObjectType type)
        {
            int value = int.Parse(token.Text);

            int count = lastObject.GetCount(type);
            if (value < 1 || value > count)
                throw new QnlException(token, count);
            
            return value;
        }        

        IQuranObject qObject = Book.Instance;
        IQuranObject lastObject;
        internal void AddPath(Token obj, QuranObjectType objType, Token value)
        {
            int iValue = int.Parse(value.Text);

            int count = qObject.GetCount(objType);

            if (iValue < 1 || iValue > count)
                throw new QnlException(value, count);

            lastObject = qObject;
            qObject = qObject.GetObject(objType, iValue - 1);

            Path.Add(new PathPart
            {
                Object = objType,
                Value = qObject.GlobalNumber,
            });
        }
    }

    public sealed class ValueSpan
    {
        internal ValueSpan() { }

        public ValueSpan(int lLimit)
        {
            LowerLimit = lLimit;
        }

        public int LowerLimit { get; internal set; }
        public int? UpperLimit { get; internal set; }
    }
}